/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.dg.gaming.client.fx.gui;

import com.dg.gaming.api.common.GameModel;
import com.dg.gaming.api.common.Player;
import com.dg.gaming.api.common.PlayerList;
import com.dg.gaming.api.common.Table;
import com.dg.gaming.api.common.msg.ITransmittable;
import com.dg.gaming.api.common.msg.MsgControllerObject;
import com.dg.gaming.api.common.msg.MsgControllerProperty;
import com.dg.gaming.api.common.msg.MsgNextPlayer;
import com.dg.gaming.api.common.msg.MsgTableMessage;
import com.dg.gaming.client.TableConnectionThread;
import com.dg.gaming.client.gui.DGComponent;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import nanoxml.XMLElement;

/**
 *
 * @author dito
 */
public abstract class FXGameController extends IGameController{
   /** Link to the game model. */
    protected GameModel model;

    public void setModel(GameModel model) {
        this.model = model;
    }

    /** Link to the DGComponent this is controlling . */
    protected DGComponent DGComponent;

    /** Link to the client's connection thread. */
    protected TableConnectionThread conn;

    /** Table number which this controller is controlling. */
    protected String tableNum;

    /**
     * Reset method which is called every time a new game is created.  Each
     * sub class of this must have this method.  This method is called when the
     * game starts for the first time.
     */
    public abstract void start();

    /**
     * Default constructor class which doesn't set up the network connection.
     *
     * @param model           Model which the controller can update depending on
     *                        what the user input is.
     * @param DGComponent  The DGComponent which the controller is
     *                        listening on.
     */
   

    /**
     * Set up a network connection for this controller.
     *
     * @param conn      Link to the TableConnectionThread.
     */
    public void setConnection(TableConnectionThread conn) {
        this.conn = conn;
        this.tableNum = conn.getTableNum();
    }

    /**
     * Return the current player.
     *
     * @return  Current player's username.
     */
    public long getCurrentPlayer() {
        // if connection is not equal to null then there is access to the
        // current player etc.
        if (this.conn != null) {
            return getTable().getPlayerList().getCurrentPlayerId();
        }
        return -1L;
    }

    /**
     * Return the player num (between 0 and the number of players).
     *
     * @return   Seat number of current player.
     */
    public int getCurrentPlayerSeatNum() {
        // Check connection has been set
        if (this.conn != null) {
            Player currentPlayer = getTable().getPlayerList().getCurrentPlayer();
            if (currentPlayer != null) {
                return currentPlayer.getSeatNum();
            }
        }
        return 0;
    }

    /**
     * Return the table number.
     *
     * @return   table number.
     */
    public String getTableNum() {
        return this.tableNum;
    }

    /**
     * Convience method for returning a table.
     *
     * @return   Current table i.e. tableList.getTable (current table).
     */
    public Table getTable() {
        return this.conn.getTable();
    }

    /**
     * Return the seat number of a user.
     *
     * @return   If -1 user is standing, otherwise seatNum &gt=0 and &lt number
     * of seats available.
     */
    public int getSeatNum() {
        if (this.conn != null) {
            PlayerList playerList = getTable().getPlayerList();
            Player player = playerList.getPlayer(this.conn.getUserid());

            if (player != null) {
                return player.getSeatNum();
            }
        }
        return Player.NOT_SEATED;
    }

    /**
     * Return the player name from a seat num.
     *
     * @param  seatNum  Seat number of the user.
     * @return   If -1 user is standing, otherwise seatNum &gt=0 and &lt number
     * of seats available.
     */
    public long getPlayer(int seatNum) {
        if (this.conn != null) {
            Table table = this.conn.getTableList().getTable(this.tableNum);
            return table.getPlayerList().getPlayer(seatNum).getPlayerId();
        }
        return -1L;
    }

    /**
     * Return the seat number of a specified player.
     *
     * @param player	Name of the player.
     * @return          Number of specified players seat.
     */
    public int getSeatNum(long player) {
        if (this.conn != null) {
            Table table = this.conn.getTableList().getTable(this.tableNum);
            return table.getPlayerList().getPlayer(player).getSeatNum();
        }
        return Player.NOT_SEATED;
    }

    /**
     * Return true/false if the game is started or not.
     *
     * NOTE: This will return true even if a player is not seated at a table.
     *
     * @return 	Returns true if a game is underway.
     */
    public boolean isGamePlaying() {
        if (this.conn != null) {
            Table table = this.conn.getTableList().getTable(this.tableNum);
            if (table != null) {
                return table.isGamePlaying();
            }
        }
        return false;
    }

    /**
     * Returns true if game is playing AND player is also seated - useful
     * for non-turned based style games.
     *
     * @return
     */
    public boolean isPlayerPlaying() {
        if (this.conn != null) {
            Table table = this.conn.getTableList().getTable(this.tableNum);
            if (table != null) {
                return table.isGamePlaying() &&
                        getSeatNum() != Player.NOT_SEATED;
            }
        }
        return false;
    }

    /**
     * Send a more complex object (any object which implements the ITransmittable
     * interface). The recieve (XMLElement element) method must be overwritten
     * to recieve it.
     *
     * @param object   A more complex piece of data encoded in XML.
     */
    public void sendObject(ITransmittable object) {
        sendObject(object.flatten());
    }

    /**
     * Send a more complex object (any object which implements the ITransmittable
     * interface).  The recieve (XMLElement element) method must be overwritten
     * to recieve it.
     *
     * @param message   A more complex piece of data encoded in XML.
     */
    public void sendObject(XMLElement message) {
        // Create property communications object, add data and send to server
        MsgControllerObject commObject = new MsgControllerObject();
        commObject.setData(message);
        this.conn.send(commObject);
    }

    /**
     * Send a normal String valued property.
     *
     * @param key
     * @param value
     */
    public void sendProperty(String key, String value) {
        // Create property communications object and send to server
        MsgControllerProperty commContProp = new MsgControllerProperty(
                MsgControllerProperty.TYPE_STRING, key, value);

        this.conn.send(commContProp);
    }

    /**
     * Send a single integer property.
     *
     * @param key
     * @param value
     */
    public void sendProperty(String key, int value) {
        // Create property communications object and send to server
        String valueStr = String.valueOf(value);
        MsgControllerProperty commContProp = new MsgControllerProperty(
                MsgControllerProperty.TYPE_INT, key, valueStr);
        this.conn.send(commContProp);
    }

    /**
     * Send a co-ordinate property (x and y integer values).
     *
     * @param key
     * @param x
     * @param y
     */
    public void sendProperty(String key, int x, int y) {
        // Create property communications object and send to server
        MsgControllerProperty commContProp = new MsgControllerProperty(
                MsgControllerProperty.TYPE_INT_TWO, key, x + " " + y);
        this.conn.send(commContProp);
    }

    /**
     * Adapter method for receiving property as a key and value from another client.
     *
     * @param key    Key of property to be read from server.
     * @param value  String value of property to be read from server.
     */
    public void receiveProperty(String key, String value) {
        // over written in sub class
    }

    /**
     * Recieve property as a String key and integer value.
     *
     * @param key    Key of property to be read from server.
     * @param value  Integer value of property to be read from server.
     */
    public void receiveProperty(String key, int value) {
        // over written in sub class
    }

    /**
     * Recieve property from another client as a co-ordinate (x and y integer).
     *
     * @param key    Key of property to be read from server.
     * @param x      Integer x co-ordinate value property to be read from server.
     * @param y      Integer y co-ordinate value property to be read from server.
     */
    public void receiveProperty(String key, int x, int y) {
        // over written in sub class
    }

    /**
     * Recieve object as XML from a client or server (use this when more advanced
     * data is required from the server or a client).
     *
     * @param message   Receive a more complex XML object from a server.
     */
    public void receiveObject(XMLElement message) {}

    /**
     * Return new if it is this players turn.
     *
     * @return  True if this current players turn.
     */
    public boolean isThisPlayersTurn() {
        if (PlayerList.NO_PLAYER == getCurrentPlayer()) {
            return false;
        } else {
            return (getSeatNum() == getCurrentPlayerSeatNum());
        }
    }

    /**
     * Method which tells the server that it is next players turn.
     */
    public void nextPlayer() {
        nextPlayer(null);
    }

    public void nextPlayer(MsgTableMessage childMsg) {
        // Create communcations table action of type "Next Player".
        MsgNextPlayer nextPlayer = new MsgNextPlayer(childMsg);

        // Fix for bug 1176951 - "Possibility of consequent moves"
        conn.getTable().getPlayerList().setCurrentPlayer(PlayerList.NO_PLAYER);

        // send to server from client
        this.conn.send(nextPlayer);
    }

    //==========================================================================
    // Empty implementations of "MouseListener"
    //==========================================================================

    /**
     * Invoked when the mouse enters a DGComponent.
     *
     * @param e
     */
    public void mouseEntered(MouseEvent e) {}

    /**
     * Invoked when the mouse exits a DGComponent.
     *
     * @param e
     */
    public void mouseExited(MouseEvent e) {}

    /**
     * Invoked when the mouse has been clicked on a DGComponent.
     *
     * @param e
     */
    public void mouseClicked(MouseEvent e) {}

    /**
     * Invoked when a mouse button has been released on a DGComponent.
     *
     * @param e
     */
    public void mouseReleased(MouseEvent e) {}

    /**
     * Invoked when a mouse button has been pressed on a DGComponent.
     *
     * @param e
     */
    public void mousePressed(MouseEvent e) {}

    //==========================================================================
    // Empty implementations of "MouseMotionListener"
    //==========================================================================

    /**
     * Invoked when the mouse button has been moved on a DGComponent
     * (with no buttons no down).
     *
     * @param e
     */
    public void mouseMoved(MouseEvent e) {}

    /**
     * Invoked when a mouse button is pressed on a DGComponent and
     * then dragged.
     *
     * @param e
     */
    public void mouseDragged(MouseEvent e) {}

    //==========================================================================
    // Empty implementations of "KeyListener"
    //==========================================================================

    /**
     * Invoked when a key has been pressed.
     *
     * @param e
     */
    public void keyPressed(KeyEvent e) {}

    /**
     * Invoked when a key has been released.
     *
     * @param e
     */
    public void keyReleased(KeyEvent e) {}

    /**
     * Invoked when a key has been typed.
     *
     * @param e
     */
    public void keyTyped(KeyEvent e) {}
}
